In-app HTML file viewing (sandboxed iframe)#4
Merged
Conversation
One-pager + complete plan for displaying/presenting AI-generated HTML files in attn via a sandboxed iframe over the existing attn:// protocol (zero new deps). Security model: iframe sandbox + IPC-bridge fencing (subframe guard + main-frame capability token) + a CSP that allows external fonts/CDN animation libs while blocking local-file exfiltration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Hierarchical epic attn-zb5 (1 epic, 4 features, 15 tasks) derived from
planning/{one-pager,complete-plan}.md. Graph: backend plumbing (F1) and
sandbox hardening (F3) run in parallel; the frontend viewer (F2) depends
on both so allow-scripts never ships before the IPC bridge is fenced;
live reload (F4) depends on F2. Every impl task carries a Verification
section; every feature ends with an integration/E2E validation task.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render .html/.htm in a native <iframe sandbox="allow-scripts"> over the existing attn:// protocol, so AI-generated HTML (inline JS, custom fonts, CDN animation libs) displays in-app. Zero new dependencies; release binary 30.20 MiB (< 32 MiB gate). Backend (src/): FileType::Html + detection; is_previewable() shared by both tree filters; mime_from_extension lowercased + font/wasm types; per-session IPC capability token (getrandom) injected into the main frame only and required on privileged IPC (ipc_message_authorized, unit-tested); a subframe bridge guard injected into all frames (window.webkit/ipc removed in iframes); query-string strip on the file-serve branch for cache-busting; CSP on .html responses allowing remote fonts/CDN libs but blocking local-file fetch; ACAO scoped to markdown/text so a sandboxed iframe can't canvas-read local images. Frontend (web/): 'html' FileType + extension map + icon; new HtmlViewer.svelte; routed in App.svelte; live-reload via a reactive SvelteMap of mtimes feeding a ?v= cache-bust; DirectoryOverview html count. Verified: cargo test (413 ok), svelte-check (0 errors), web unit tests, and a live E2E — iframe renders, an attack page's edit_save/quit are rejected by the token (the native WebKit bridge is reachable on macOS, so the token is the load-bearing control), and editing the file on disk live-reloads the view. Adversarially reviewed by a multi-agent workflow; all 9 confirmed findings addressed. Implements epic attn-zb5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop HtmlViewer's bespoke header bar so HTML files share the exact same PathBreadcrumb chrome as every other file type. Add an "Open in browser" icon button (Lucide external-link) to the breadcrumb's top-right cluster, shown for HTML files (where the Share button sits for markdown). Verified: plan.html renders in the sandboxed iframe with the breadcrumb header, 1 open-in-browser button, 0 share buttons, and no leftover header bar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Center the header action buttons vertically (-mt-3): the 27px button previously overflowed the 40px header by 2px (15px above / -2px below); now ~6px above and below. - Hide the iframe's chunky native scrollbar to match the app's clean look. Cross-origin sandboxed content can't be scrolled by attn's ScrollArea (content height isn't measurable) nor styled, so the iframe is widened by the measured platform scrollbar width inside an overflow-hidden wrapper — the native bar falls in the clipped gutter (0 for overlay scrollbars). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The current .beads/.gitignore predated the Dolt sql-server runtime files, so
.beads-credential-key (a secret), dolt-server.{lock,log,pid,port}, and backup/
showed up as untracked — the credential key especially must never be committed.
Add ignore patterns for them. Also commits the tracked interactions.jsonl
(bd's shared audit log) updated this session.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- web/src/lib/ipc.ts: the module-level capability-token read touched `window`
at import time, crashing the Node test env ("window is not defined") and
failing 6 component test files. Guard it (and send()) with `typeof window`.
- src/main.rs: use the `['?', '#']` array char pattern instead of a closure
(clippy::manual_pattern_char_comparison under -D warnings).
- cargo fmt across src/ (main.rs/ipc.rs from this feature; daemon.rs/manager.rs/
ws.rs were pre-existing drift the fmt --check gate also flagged).
Verified: cargo fmt --check clean, cargo clippy --all-targets -D warnings clean,
web npm test 31 files / 0 failures.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an in-app viewer for
.html/.htmfiles, alongside the existing markdown/image/video/audio viewers — primarily to showcase AI-generated HTML (inline JS, custom web fonts, CDN animation libraries). Files render in a native<iframe sandbox="allow-scripts">loaded via the existingattn://custom protocol.planning/one-pager.md,planning/complete-plan.md. Work tracked as Beads epicattn-zb5(closed).What's included
Backend (
src/)FileType::Html+.html/.htmdetection;is_previewable()now shared by both tree-filter sites (removes a drift hazard);mime_from_extensionlowercased + font/wasm/avif types.getrandom) injected only into the main app frame and required on privileged IPC (ipc_message_authorized, unit-tested).with_initialization_script_for_main_only(_, false)) — stripswindow.webkit/window.ipcinside iframes..htmlresponses: allows remote fonts/CDN libs (https:/data:) but blocks local-filefetch()(noattn:inconnect-src).Access-Control-Allow-Origin: *scoped to markdown/text only so a sandboxed iframe can't canvas-read local images.?v=<mtime>cache-busting).Frontend (
web/)'html'in theFileTypeunion + extension map + icon; newHtmlViewer.svelte; routed inApp.svelte.SvelteMapof file mtimes feeds a?v=cache-bust so on-disk edits refresh the iframe.Security model
Three independent controls let rich, styled JS pages run while staying safe:
allow-scriptswithoutallow-same-origin(opaque origin): scripts run, but can't touch the app DOM/storage, navigate top, popups, or forms.fetch()and cross-origin image reads blocked.Verification
cargo test— 413 passed, 0 failed (incl. the token-gate unit tests).cd web && npm run check— 0 errors;npm test— pass.edit_save/quitare rejected by the token (file byte-identical, daemon survives); editing the file on disk live-reloads the view; the app's own navigation/search IPC works.task check:size— PASS, 30.20 MiB < 32 MiB.Out of scope (v1)
A strict offline-only/no-network mode (future per-file toggle), editing HTML, and comments/collab on HTML (the review layer is markdown-anchored).
🤖 Generated with Claude Code